/*
 * Copyright 2008 Neil Bacon
 * Distributed under the terms of the Mozilla Public License version 1.1
 * or the GNU General Public License version 3.
 */

package nb.xadd.node;

import nb.xadd.node.impl.BooleanDecisionNode;
import nb.xadd.node.impl.ConstantValueNode;
import nb.xadd.node.impl.LinearValueNode;
import nb.xadd.node.impl.PolynomialValueNode;
import nb.xadd.node.impl.RealEqualDecisionNode;
import nb.xadd.node.impl.RealGreaterOrEqualDecisionNode;
import nb.xadd.operator.BinaryOperator;
import nb.xadd.operator.impl.AbstractBinaryOperator;

/** 
 * Node in an Extended Algebraic Decision Diagram tree.
 * <p>
 * The {@link #apply(BinaryOperator, Node)} methods implement double dispatch
 * ({@link http://en.wikipedia.org/wiki/Double_dispatch})
 * for binary operators.
 * This avoids big nested tests on Node types at the cost of many small methods.
 * <p>
 * With the types of the arguments established, apply() delegates the recursive application of
 * the binary operator to the operator's execute*() methods.
 * The common recursive pattern is implemented in {@link AbstractBinaryOperator}.
 */
public interface Node extends Comparable<Node> {

    /** Ordering of nodes in tree */
    public enum Ordering {
        BOOLEAN, REAL_EQUAL, READ_GREATER_OR_EQUAL, CONSTANT, LINEAR, POLYNOMIAL
    }

    Ordering getOrdering();

    /**
     * Get unique ID
     * @return ID returned by Object.hashCode()
     */
    int getId();

    /**
     * Append String description of sub-tree to buf.
     * Used by {@link #toString()}.
     * @param depth
     * @param buf
     */
    void appendTo(int depth, StringBuilder buf);

    /**
     * One line description of test for debugging.
     * Excludes children, unlike {@link #toString()} and {@link #appendTo(int, StringBuilder)}.
     * Excludes trailing new line.
     * @return test description
     */
    String description();

    /**
     * Apply a Boolean BinaryOperator to this and node.
     * Implemented using double dispatch to the following apply*() methods.
     * @param binaryOperator
     * @param node
     * @return result Node
     */
    Node apply(BinaryOperator binaryOperator, Node node);

    /**
     * Apply an BinaryOperator to this and a ConstantValueNode.
     * @param binaryOperator
     * @param node
     * @return result Node
     */
    Node applyConstantValue(BinaryOperator binaryOperator, ConstantValueNode node);

    /**
     * Apply an BinaryOperator to this and a LinearValueNode.
     * @param binaryOperator
     * @param node
     * @return result Node
     */
    Node applyLinearValue(BinaryOperator binaryOperator, LinearValueNode node);

    /**
     * Apply an BinaryOperator to this and a PolynomialValueNode.
     * @param binaryOperator
     * @param node
     * @return result Node
     */
    Node applyPolynomialValue(BinaryOperator binaryOperator, PolynomialValueNode node);

    /**
     * Apply an BinaryOperator to this and a BooleanDecisionNode.
     * @param binaryOperator
     * @param node
     * @return result Node
     */
    Node applyBooleanDecision(BinaryOperator binaryOperator, BooleanDecisionNode node);

    /**
     * Apply an BinaryOperator to this and a RealGreaterOrEqualDecisionNode.
     * @param binaryOperator
     * @param node
     * @return result Node
     */
    Node applyRealGreaterOrEqualDecision(BinaryOperator binaryOperator, RealGreaterOrEqualDecisionNode node);

    /**
     * Apply an BinaryOperator to this and a RealEqualDecisionNode.
     * @param binaryOperator
     * @param node
     * @return result Node
     */
    Node applyRealEqualDecision(BinaryOperator binaryOperator, RealEqualDecisionNode node);
}
